跳到主要内容

页面大量dom的优化

· 阅读需 5 分钟

关于 dom 优化的情况

页面中有大量 dom 需要优化的情况可能分为 2 种,一种是大量的 dom 刚好铺满一个屏幕,另外一种是大量的 dom 渲染到一个页面上并且超出了一个屏幕。

对于前者,举例一种情况就是需要渲染几百个图片在一个完整的页面上,可能看到几百个图片的 img 标签并不是像百万级那么夸张,但是对于这样的渲染仍然还是有很多值得优化的空间。

对于后者,在前者的基础上还会加入一个对滚动时的优化,因为 dom 非常的多导致滚动的时候会掉帧,那么需要进行滚动优化,虚拟化列表就是其中的一个解决方法。

对于滚动采用虚拟化列表即可解决了,这里只讲解如何优化 dom 的渲染性能

dom 的渲染优化

如果我们要渲染几百个页面,现在我们用原生的 js 来做演示,那么我们需要创建几百个 dom 之后 append 到父元素中。

const parent = document.getElementById("parent")

//循环800次
for(let i = 0; i < 800; i++){
const img = document.createElement("img")
img.src = "....jpg"
parent.appendChild("img")
}

这样的逻辑会创建 800 个 dom 并且渲染到这个 parent 容器中(parent 是一个 div 标签)。当我们执行的时候,如果机器性能不是及其顶尖,那么你会发现页面会处于一个较长时间的加载中,并且你会发现如果你放一个按钮在页面上,这个时候你鼠标点击按钮是不能相应的,因为主线程正在被占用来渲染 800 个 dom。到这里就出现了第一个优化的点异步加载

异步加载

异步加载我们就要使用到异步任务来进行 dom 的渲染,其中 setTimeout 这个定时器 api 就可以实现异步任务,setTimeout 将执行的函数放入到异步任务队列中,当主线程的任务执行完成之后就进入到异步任务来执行这个任务,所以我们的主线程始终是不会被阻塞的。

function createDOM(){
setTimeout(()=>{
const img = document.createElement("img")
img.src = "....jpg"
parent.appendChild("img")
},50)
}

经过上面的封装,我们把 createDOM 封装成了一个创建一个异步任务来渲染一次 dom 的逻辑,那么我们要渲染 800 个就需要调用 800 次 createDOM。其中设置定时器的时间间隔为 50 毫秒,那么每隔 50 毫秒就会创建一个 dom 并添加到父容器中,并且会发现它在渲染的时候我们的鼠标以及页面都不会被阻塞卡住,仍然可以触发一些事件。

提到了异步加载,还有一个比较重要的 api,requestAnimationFrame。这个 api 在动画中也很常用,它的作用是在一帧渲染之后执行函数,至于每秒渲染多少帧,通常根据屏幕目前刷新率来确定。在更多情况下使用这个 api 具有更大的优势。

const count = 800
let current = 0
function createDOM(){
if(current >= count) return

const img = document.createElement("img")
img.src = "....jpg"
parent.appendChild("img")

current++
requestAnimationFrame(createDOM)
}
requestAnimationFrame(createDOM)

requestAnimationFrame 的讲解